
#include "CCB.h"

#include "config.h"
#include "readWriteFile.h"
#include "archivingFiles.h"

#include <system_error>
#include <boost/process/system.hpp>


CCB* CCB::s_instance = nullptr;



const_bool CCB::checkFlagMatchesSetFlags(const_str textFlags, str& compilerText, int& compilerIndex) {
    FOR_SIZE_T_ADDREVIATED(i, LangCompilers.size()) {   
        string flags = LangCompilers[i].first + ' ' + LangCompilers[i].second;

        if (flags == textFlags || LangCompilers[i].first == textFlags) {
            if (flags == textFlags) compilerIndex = i;
            else                    compilerText = LangCompilers[i].second;
            return true;
        }
    }

    bot->getApi().sendMessage(chatId, textError2);
    return false;
}

void CCB::compilerFile(const_str command, function<void()> function) {
    error_code ec;
    const_int resulte = boost::process::system(command, ec);
    if (!resulte) function();
    else          bot->getApi().sendMessage(chatId, textError4);
}

void CCB::sendDocument(const_ch* text) {
    InputFile::Ptr inputFile = InputFile::fromFile(text, text);
    bot->getApi().sendDocument(chatId, inputFile, "", textComplite);
}

const_str CCB::searchText(size_t_c startNumber, size_t_c endNumber, const_str text) {
    str textFind;
    FOR_SIZE_T(i, startNumber, endNumber) textFind += text[i];
    return textFind;
}

const_str CCB::ifUserNotCompiler(const_str compilerText, size_t_c compilerIndex, const_str nameFile) {
    const_str commandCompiler = ' ' + nameFile + standartCommandCompiler;
    if (!compilerText.empty()) return compilerText + commandCompiler;
    return LangCompilers[compilerIndex].second + commandCompiler;
}

const InlineKeyboardButton::Ptr CCB::createInlineKeyboardButton(const_str textName, const_str textData) {
    InlineKeyboardButton::Ptr button(new TgBot::InlineKeyboardButton);
    button->text = textName;
    button->callbackData = textData;
    return button;
}

void CCB::sendMessagePrintWorkCode(const_pair_str textTrue, const_pair_str textFalse) {
    InlineKeyboardMarkup::Ptr keyboard(new TgBot::InlineKeyboardMarkup);

    const InlineKeyboardButton::Ptr buttonYes = createInlineKeyboardButton(textTrue.first, textTrue.second); 
    const InlineKeyboardButton::Ptr buttonNo  = createInlineKeyboardButton(textFalse.first, textFalse.second); 

    keyboard->inlineKeyboard.push_back({ buttonYes, buttonNo });
    bot->getApi().sendMessage(chatId, textQuestionPrintProgram, false, 0, keyboard);
}


void CCB::TextCompiler(Message::Ptr message) {
    size_t_c  sizeMesText = message->text.size();
    const_int endFlags    = message->text.find('\n') == str::npos? sizeMesText : message->text.find('\n');
    const_str textFlags   = searchText(message->text.find(' ')+1, endFlags, message->text);    
    const_str fileContent = message->text.substr(endFlags + 1, sizeMesText - endFlags - 1);
    str       compilerText;
    int       compilerIndex;
    
    if (endFlags == sizeMesText) {
        bot->getApi().sendMessage(chatId, textError3);
        return;
    }

    if (!checkFlagMatchesSetFlags(textFlags, compilerText, compilerIndex)) return;
    
    createFile(standartPath, fileContent);
    
    compilerFile(ifUserNotCompiler(compilerText, compilerIndex), [this]() {
        archivingFolder(folderSavefiles);
        sendDocument(standartFileZip);
        deleteArchive();
        sendMessagePrintWorkCode(LogicalAnswers[0], LogicalAnswers[1]);
    });
}

void CCB::createAndCompilerFileCompiler(Message::Ptr message, const_str compiler, const_str filePath) {
    const File::Ptr file     = bot->getApi().getFile(message->document->fileId);
    const_str       textFile = bot->getApi().downloadFile(file->filePath);
    
    createFile(filePath, textFile);

    compilerFile(compiler, [this, filePath]() {
        const_str test = str(folderSavefiles) + str(standartFileComplete);
        sendDocument(test.c_str());
        sendMessagePrintWorkCode(LogicalAnswers[0], LogicalAnswers[1]);
        deleteFile(filePath.c_str());
    });
}

void CCB::FileCompiler(Message::Ptr message) {
    if (message->document) {
        size_t_c        startSizeFormatFile = message->document->fileName.find('.');
        size_t_c        endSizeFormatFile   = message->document->fileName.size();
        const_str       textFormatFile      = searchText(startSizeFormatFile+1, endSizeFormatFile, message->document->fileName);
        const_str       caption             = message->caption;
        const_str       filePath            = folderSavefiles + message->document->fileName;
        
        if (caption.empty()) {
            FOR_SIZE_T_ADDREVIATED(i, LangCompilers.size()) {
                if (textFormatFile == LangCompilers[i].first) {
                    const_str commandGpp = LangCompilers[i].second + ' ' + filePath + standartCommandCompiler;
                    createAndCompilerFileCompiler(message, commandGpp, filePath);
                }
            }
        } else {
            str compilerText;
            int compilerIndex;
            if (!checkFlagMatchesSetFlags(caption, compilerText, compilerIndex)) return;
            createAndCompilerFileCompiler(message, ifUserNotCompiler(compilerText, compilerIndex, filePath), filePath);
        }
    }
}


void CCB::printWorkProgram(const_int chatId) {
    size_t_c sizeBuffer = 1024;
    str result;

    FILE* pipe = popen("files/main", "r");
    if (pipe) {
        char buffer[sizeBuffer];
        while (!feof(pipe)) {
            if (fgets(buffer, sizeBuffer, pipe) != nullptr) result += buffer;
        }
        pclose(pipe);
    }
    
    bot->getApi().sendMessage(chatId, textPrintProgram + result);
}

void CCB::submit(CallbackQuery::Ptr callbackQuery, const_ch* CheackTextOnButton, 
        function<void(const_int)> functionTrue, function<void(const_int)> functionFalse) {
    
    const_int chatId = callbackQuery->message->chat->id;
    if (callbackQuery->data == CheackTextOnButton) functionTrue(chatId);
    else                                           functionFalse(chatId);
}

void CCB::commmand() {
    bot->getEvents().onCommand("start", [this](Message::Ptr message) { 
        bot->getApi().sendMessage(message->chat->id, textStart);
    });

    bot->getEvents().onCommand("help", [this](Message::Ptr message) { 
        bot->getApi().sendMessage(message->chat->id, textHelp);
    });

    bot->getEvents().onCommand("about", [this](Message::Ptr message) { 
        bot->getApi().sendMessage(message->chat->id, textAbout); 
    });

    bot->getEvents().onCommand("textCompiler", [this](Message::Ptr message) { 
        chatId = message->chat->id;
        TextCompiler(message); 
    });

    bot->getEvents().onAnyMessage([this](Message::Ptr message) { 
        chatId = message->chat->id;
        FileCompiler(message);
    });

    bot->getEvents().onCallbackQuery([this](CallbackQuery::Ptr callbackQuery) {
        submit(callbackQuery, "yes", [this](const_int chatId) {
            printWorkProgram(chatId);
        }, [this](const_int chatId) {});

        bot->getApi().editMessageReplyMarkup(callbackQuery->message->chat->id, callbackQuery->message->messageId, callbackQuery->message->text, 0);
    });

}

void CCB::run() {
    commmand();

    try {
        cout << "Bot username: " << bot->getApi().getMe()->username << endl;

        TgLongPoll longPoll(*bot);
        while (true) longPoll.start();

    } catch (TgException& e) {
        cout << "error: " << e.what() << endl;
    }
}

CCB* CCB::getCCB(const_ch* token) {
    if (!s_instance) {
        s_instance = new CCB(token);
    }
    return s_instance;
}

CCB::CCB(const_ch* token) { bot = new Bot(token); }

CCB::~CCB() {}

